package com.ssy.lingxi.order.serviceimpl.openapi;

import com.ssy.lingxi.common.constant.manage.ShopEnvironmentEnum;
import com.ssy.lingxi.common.constant.manage.ShopTypeEnum;
import com.ssy.lingxi.common.constant.order.OrderModeEnum;
import com.ssy.lingxi.common.constant.order.OrderSourceKindEnum;
import com.ssy.lingxi.common.constant.order.OrderTradeProcessTypeEnum;
import com.ssy.lingxi.common.constant.order.OrderTypeEnum;
import com.ssy.lingxi.common.constant.product.PriceTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.api.model.openapi.request.*;
import com.ssy.lingxi.order.entity.*;
import com.ssy.lingxi.order.model.bo.*;
import com.ssy.lingxi.order.model.constant.*;
import com.ssy.lingxi.order.model.dto.CountryAreaDTO;
import com.ssy.lingxi.order.repository.*;
import com.ssy.lingxi.order.service.base.IBaseOrderDeliveryService;
import com.ssy.lingxi.order.service.base.IBaseOrderRequirementService;
import com.ssy.lingxi.order.service.base.IBaseOrderTaskService;
import com.ssy.lingxi.order.service.openapi.IOrderOpenApiService;
import com.ssy.lingxi.order.service.web.IVendorOrderService;
import com.ssy.lingxi.order.utils.NumberUtil;
import com.ssy.lingxi.order.utils.OrderStringUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * OpenApi - 订单相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-10-16
 */
@Service
public class OrderOpenApiServiceImpl implements IOrderOpenApiService {
    @Resource
    private OrderRepository orderRepository;

    @Resource
    private OrderProductRepository orderProductRepository;

    @Resource
    private OrderConsigneeRepository orderConsigneeRepository;

    @Resource
    private OrderInvoiceRepository orderInvoiceRepository;

    @Resource
    private OrderRequirementRepository orderRequirementRepository;

    @Resource
    private CountryAreaRepository countryAreaRepository;

    @Resource
    private OrderDeliveryRepository orderDeliveryRepository;

    @Resource
    private OrderDeliveryProductRepository orderDeliveryProductRepository;

    @Resource
    private IBaseOrderTaskService baseOrderTaskService;

    /**
     * 创建订单（单个订单）
     *
     * @param orderVO 接口参数
     * @return 创建结果
     * @see com.ssy.lingxi.order.service.web.IOrderCreationService
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> createOpenApiOrder(OpenApiOrderVO orderVO) {
        //*****************************************************
        //Step 1: 查询、新增供应商交易流程（可以写死流程，这里略过）
        //    a. 根据项目需求而定
        //    b. 这里固定流程，代码写死
        //*****************************************************

        //创建订单
        OrderDO order = new OrderDO();
        order.setCreateTime(LocalDateTime.now());
        order.setSubmitTime(LocalDateTime.now());
        //这里是配送时间段，默认为当前日期，可为空字符串，格式为 yyyy-MM-dd，或yyyy-MM-dd HH:mm，或yyyy-MM-dd HH:mm-HH:mm
        order.setDeliverPeriod("");
        order.setBuyerMemberId(orderVO.getBuyerMemberId());
        order.setBuyerRoleId(orderVO.getBuyerRoleId());
        //创建订单的用户Id（如没有数据权限功能，可以赋值为0）
        order.setBuyerUserId(0L);
        order.setBuyerMemberName(orderVO.getBuyerMemberName());
        order.setVendorMemberId(orderVO.getVendorMemberId());
        order.setVendorRoleId(orderVO.getVendorRoleId());
        order.setVendorMemberName(orderVO.getVendorMemberName());

        //订单号（随机生成字符串）
        order.setOrderNo(OrderStringUtil.randomUniqueNumber());
        //订单来源商城要根据项目需求而定
        order.setShopId(orderVO.getShopId());
        order.setShopType(ShopTypeEnum.ENTERPRISE.getCode());
        order.setShopEnvironment(ShopEnvironmentEnum.WEB.getCode());
        order.setShopName("瓴犀企业商城");

        //订单模式、订单类型
        order.setOrderMode(OrderModeEnum.BUYER.getCode());
        order.setOrderType(OrderTypeEnum.SPOT_PURCHASING.getCode());

        //订单种类（这个字段是订单服务自有的，其他服务无关，方便代码判断）
        order.setOrderKind(OrderSourceKindEnum.BUYER.getCode());

        //订单摘要（可以自定义，默认为第一个商品的名称）
        order.setDigest(orderVO.getProducts().stream().filter(product -> StringUtils.hasLength(product.getName())).map(OpenApiProductVO::getName).findFirst().orElse(""));

        order.setQuoteId(0L);
        order.setQuoteNo("");
        order.setLogisticsNo("");

        order.setCanRefund(false);
        order.setHasPaid(false);
        order.setHasDelivered(false);
        order.setHasReceived(false);
        order.setBuyerCommented(false);
        order.setVendorCommented(false);
        //拆单、转单（根据项目需求，将“上级供应商”的会员Id、角色Id设置为0，将不会触发后续代码的转单功能）
        order.setSupplyMemberId(0L);
        order.setSupplyRoleId(0L);
        order.setSupplyMemberName("");
        //在转单的时候再改变拆单类型，生成关联的订单Id
        order.setSeparateType(OrderSeparateTypeEnum.DEFAULT.getCode());
        order.setRelationId(0L);
        order.setRelationNo("");
        //营销活动状态
        order.setPromotionStatus(OrderPromotionStatusEnum.NONE.getCode());
        //拼团订单的拼团Id
        order.setGroupId(0L);


        // 保存订单，以生成订单的Id
        orderRepository.saveAndFlush(order);

        //检查商品信息，获得订单总金额
        Wrapper<OrderProductCheckBO> productCheckResult = checkOpenApiOrderProduct(order, orderVO.getProducts(), true);
        if(productCheckResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(productCheckResult.getCode(), productCheckResult.getMessage());
        }

        // 如果订单商品包含了“物流”配送方式，订单接收人信息不能为空
        if(productCheckResult.getData().getNeedConsignee() && orderVO.getConsignee() == null) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.ORDER_CONSIGNEE_MUST_NOT_EMPTY);
        }

        //订单商品总额
        order.setProductAmount(productCheckResult.getData().getProductAmount());
        // 运费
        order.setFreight(NumberUtil.notNullAndPositive(orderVO.getFreight()) ? orderVO.getFreight() : BigDecimal.ZERO);
        // 活动优惠金额
        order.setPromotionAmount(BigDecimal.ZERO);
        // 优惠券优惠金额
        order.setCouponAmount(BigDecimal.ZERO);
        // 订单总价
        order.setTotalAmount(order.getProductAmount().add(order.getFreight()).subtract(order.getPromotionAmount()).subtract(order.getCouponAmount()).setScale(2, RoundingMode.HALF_UP));
        // 订单“已支付”的金额
        order.setPaidAmount(BigDecimal.ZERO);

        //*****************************
        //工作流任务
        //****************************
        Wrapper<Void> processResult = checkOrderProcessTask(order);
        if(processResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(processResult.getCode(), processResult.getMessage());
        }

        //执行订单工作流
        Wrapper<ProcessTaskBO> taskResult = order.getTask().getSkipFirstStep() ? baseOrderTaskService.startOrderProcessThenExecFirstTask(order) : baseOrderTaskService.startOrderProcess(order);
        if(taskResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(taskResult.getCode(), taskResult.getMessage());
        }

        // 支付记录（这里不需要支付，如需要支付请参考相关 IOrderCreationService 中创建订单的相关方法）

        //合同信息（这里没有合同，如需要合同请参考相关 IOrderCreationService 中创建订单的相关方法）

        //发票信息
        Wrapper<Void> invoiceResult = checkOrderInvoice(order, orderVO.getInvoice(), true);
        if(invoiceResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(invoiceResult.getCode(), invoiceResult.getMessage());
        }

        //收货人信息
        Wrapper<Void> consigneeResult = checkOrderConsignee(order, orderVO.getConsignee(), true);
        if(consigneeResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(consigneeResult.getCode(), consigneeResult.getMessage());
        }

        // 其他要求
        Wrapper<Void> requirementResult = checkOrderRequirement(order, orderVO.getRequirement(), true);
        if(requirementResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(requirementResult.getCode(), requirementResult.getMessage());
        }

        //发货记录
        order.setDeliveries(new HashSet<>());

        // 保存订单
        orderRepository.saveAndFlush(order);
        return Wrapper.success();
    }

    /**
     * 校验订单商品
     *
     * @param order    订单
     * @param products 订单商品列表
     * @param isCreate 新增订单-true, 修改订单-false
     * @return 订单总金额，是否需要物流
     * @see com.ssy.lingxi.order.service.base.IBaseOrderProductService
     */
    @Override
    public Wrapper<OrderProductCheckBO> checkOpenApiOrderProduct(OrderDO order, List<OpenApiProductVO> products, boolean isCreate) {
        //商品总金额
        BigDecimal productAmount = BigDecimal.ZERO;
        //是否包含“物流”配送方式
        boolean needLogistics = false;
        //根据项目需求：购物车Id列表
        List<Long> cartIds = new ArrayList<>();

        List<OrderProductDO> productList = new ArrayList<>();
        for (OpenApiProductVO orderProductVO : products) {
            //根据商品中的“配送方式”判断订单是否需要物流，只要有一个商品需要物流，订单就需要物流
            needLogistics = needLogistics || orderProductVO.getDeliveryType().equals(OrderProductDeliverTypeEnum.LOGISTICS.getCode());

            OrderProductDO product = new OrderProductDO();
            product.setOrder(order);
            product.setPriceType(orderProductVO.getPriceType());
            product.setProductId(orderProductVO.getProductId());
            product.setSkuId(orderProductVO.getSkuId());
            product.setStockId(0L);
            product.setCartId(0L);
            //商品编号
            product.setProductNo("");
            product.setName(orderProductVO.getName());
            product.setCategory(orderProductVO.getCategory());
            product.setBrand(StringUtils.hasLength(orderProductVO.getBrand()) ? orderProductVO.getBrand() : "");
            product.setSpec(StringUtils.hasLength(orderProductVO.getSpec()) ? orderProductVO.getSpec() : "");
            product.setUnit(orderProductVO.getUnit());
            product.setLogo(orderProductVO.getLogo());
            //商品原价
            product.setOriginalPrice(orderProductVO.getPrice());
            //商品单价 * 折扣 = 到手价
            product.setPrice(orderProductVO.getPrice());
            product.setDiscount(NumberUtil.notNullAndPositive(orderProductVO.getDiscount()) ? orderProductVO.getDiscount() : BigDecimal.ONE);
            product.setRefPrice(orderProductVO.getPriceType().equals(PriceTypeEnum.Gift.getCode()) ? BigDecimal.ZERO : product.getPrice().multiply(product.getDiscount()).setScale(3, RoundingMode.HALF_UP));

            //供应商（卖方）的商品库存
            product.setStock(NumberUtil.isNullOrNegative(orderProductVO.getStock()) ? BigDecimal.ZERO : orderProductVO.getStock());
            product.setQuantity(orderProductVO.getQuantity());
            product.setTax(orderProductVO.getTax() == null ? false : orderProductVO.getTax());
            product.setTaxRate(NumberUtil.notNullAndPositive(orderProductVO.getTaxRate()) ? orderProductVO.getTaxRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) : BigDecimal.ZERO);
            product.setAmount(product.getRefPrice().multiply(orderProductVO.getQuantity()));
            //使用“平台优惠券”后的优惠抵扣金额
            product.setPlatformCouponAmount(BigDecimal.ZERO);
            product.setPaidAmount(BigDecimal.ZERO);
            product.setDeliverType(orderProductVO.getDeliveryType());
            product.setFreightType(NumberUtil.isNullOrNegative(orderProductVO.getFreightType()) ? OrderFreightTypeEnum.NONE.getCode() : orderProductVO.getFreightType());
            product.setWeight(NumberUtil.isNullOrNegativeZero(orderProductVO.getWeight()) ? BigDecimal.ZERO : orderProductVO.getWeight());
            product.setLogisticsTemplateId(NumberUtil.isNullOrNegativeZero(orderProductVO.getLogisticsTemplateId()) ? 0L : orderProductVO.getLogisticsTemplateId());
            product.setReceiver(StringUtils.hasLength(orderProductVO.getReceiver()) ? orderProductVO.getReceiver().trim() : "");
            product.setAddress(StringUtils.hasLength(orderProductVO.getAddress()) ? orderProductVO.getAddress().trim() : "");
            product.setPhone(StringUtils.hasLength(orderProductVO.getPhone()) ? orderProductVO.getPhone() : "");
            //转单商品标记为默认值
            product.setSeparateType(OrderSeparateTypeEnum.DEFAULT.getCode());
            product.setRelationId(0L);

            //关联报价单的商品
            product.setMaterial(null);
            //营销活动
            product.setPromotions(new HashSet<>());

            product.setDelivered(BigDecimal.ZERO);
            product.setReceived(BigDecimal.ZERO);
            product.setExchangeCount(BigDecimal.ZERO);
            product.setReturnCount(BigDecimal.ZERO);
            product.setMaintainCount(BigDecimal.ZERO);
            product.setReturnAmount(BigDecimal.ZERO);
            product.setEnhanceCount(BigDecimal.ZERO);
            product.setLeftCount(product.getQuantity());
            product.setDifferCount(BigDecimal.ZERO);

            productList.add(product);

            //商品总价
            productAmount = productAmount.add(product.getAmount());

            //购物车Id列表
            cartIds.add(product.getCartId());
        }

        //如果是更新订单，先删除原有订单商品，再新增订单商品。根据项目需求而定
        if(!isCreate) {
            orderProductRepository.deleteByOrder(order);
        }

        orderProductRepository.saveAll(productList);
        order.setProducts(new HashSet<>(productList));
        return Wrapper.success(new OrderProductCheckBO(productAmount, needLogistics, cartIds));
    }

    /**
     * 校验、生成订单工作流
     *
     * @param order 订单
     * @return 校验结果
     */
    @Override
    public Wrapper<Void> checkOrderProcessTask(OrderDO order) {
        // 工作流的初始化，请参考 OrderDataInitServiceImpl.initBaseTradeProcess() 方法中的内容
        OrderProcessTaskDO processTask = new OrderProcessTaskDO();
        // 流程类型与流程的Key（流程的Key是工作流唯一标识，如果要执行工作流，此字段必填，且要与工作流服务中的流程图定义一致）
        processTask.setProcessType(OrderTradeProcessTypeEnum.ORDER_TRADE.getCode());
        processTask.setProcessKey("order_direct_delivery");
        processTask.setTaskId("");
        // 支付次数
        processTask.setPayTimes(0);
        // 订单自动取消时间
        processTask.setExpireHours(BigDecimal.ZERO);
        // 是否跳过流程第一步（采购商手动提交到供应商）
        processTask.setSkipFirstStep(true);
        // 支付相关，根据项目需求选择流程而定，这里不需要支付
        processTask.setNextPay(false);
        processTask.setNextSerialNo(0);
        processTask.setPayStartNode(0);
        processTask.setPayEndNode(0);

        //设置订单的关联关系，由于 OrderDO 与 OrderProcessTaskDO 是一对一的单向关联，并且定义了 CascadeType.PERSIST，所以只要保存 OrderDO，会自动保存 OrderProcessTaskDO
        order.setTask(processTask);
        return Wrapper.success();
    }

    /**
     * 校验、生成订单发票
     *
     * @param order     订单
     * @param invoiceVO 订单发票接口参数
     * @param isCreate  是否新增，true-新增，false-修改
     * @return 校验结果
     * @see com.ssy.lingxi.order.service.base.IBaseOrderInvoiceService
     */
    @Override
    public Wrapper<Void> checkOrderInvoice(OrderDO order, OpenApiInvoiceVO invoiceVO, boolean isCreate) {
        if(!isCreate) {
            orderInvoiceRepository.deleteByOrder(order);
        }

        if(invoiceVO == null) {
            order.setInvoice(null);
            return Wrapper.success();
        }

        OrderInvoiceDO invoice = new OrderInvoiceDO();
        invoice.setInvoiceId(invoiceVO.getInvoiceId());
        invoice.setOrder(order);
        invoice.setInvoiceKind(invoiceVO.getInvoiceKind());
        invoice.setInvoiceType(invoiceVO.getInvoiceType());
        invoice.setTitle(invoiceVO.getTitle());
        invoice.setTaxNo(invoiceVO.getTaxNo());
        invoice.setBank(StringUtils.hasLength(invoiceVO.getBank()) ? invoiceVO.getBank().trim() : "");
        invoice.setAccount(StringUtils.hasLength(invoiceVO.getAccount()) ? invoiceVO.getAccount().trim() : "");
        invoice.setAddress(StringUtils.hasLength(invoiceVO.getAddress()) ? invoiceVO.getAddress().trim() : "");
        invoice.setPhone(StringUtils.hasLength(invoiceVO.getPhone()) ? invoiceVO.getPhone().trim() : "");
        invoice.setDefaultInvoice(invoiceVO.getDefaultInvoice());

        order.setInvoice(invoice);
        return Wrapper.success();
    }

    /**
     * 校验并生成订单收货人信息
     *
     * @param order       订单
     * @param consigneeVO 订单收货人接口参数
     * @param isCreate    是否新增，true-新增，false-修改
     * @return 操作结果
     * @see com.ssy.lingxi.order.service.base.IBaseOrderConsigneeService
     */
    @Override
    public Wrapper<Void> checkOrderConsignee(OrderDO order, OpenApiConsigneeVO consigneeVO, boolean isCreate) {
        if(!isCreate) {
            orderConsigneeRepository.deleteByOrder(order);
        }

        if(Objects.isNull(consigneeVO)) {
            order.setConsignee(null);
            return Wrapper.success();
        }

        //这里用标品的省、市、区、街道表中的地址信息来校验，根据项目需求定制
        List<String> areaCodeList = Stream.of(consigneeVO.getProvinceCode(), consigneeVO.getCityCode(), consigneeVO.getDistrictCode(), consigneeVO.getStreetCode()).collect(Collectors.toList());
        List<CountryAreaDTO> areaList = countryAreaRepository.findByCodeIn(areaCodeList, CountryAreaDTO.class);
        String provinceName = areaList.stream().filter(area -> area.getCode().equals(consigneeVO.getProvinceCode())).map(CountryAreaDTO::getName).findFirst().orElse(null);
        if(!StringUtils.hasLength(provinceName)) {
            return Wrapper.fail(ResponseCode.ORDER_PROVINCE_DOES_NOT_EXIST);
        }

        String cityName = areaList.stream().filter(area -> area.getCode().equals(consigneeVO.getCityCode())).map(CountryAreaDTO::getName).findFirst().orElse(null);
        if(!StringUtils.hasLength(cityName)) {
            return Wrapper.fail(ResponseCode.ORDER_CITY_DOES_NOT_EXIST);
        }

        String districtName = areaList.stream().filter(area -> area.getCode().equals(consigneeVO.getDistrictCode())).map(CountryAreaDTO::getName).findFirst().orElse(null);
        if(!StringUtils.hasLength(districtName)) {
            return Wrapper.fail(ResponseCode.ORDER_DISTRICT_DOES_NOT_EXIST);
        }

        String streetName = "";
        if(StringUtils.hasLength(consigneeVO.getStreetCode())) {
            streetName = areaList.stream().filter(area -> area.getCode().equals(consigneeVO.getStreetCode())).map(CountryAreaDTO::getName).findFirst().orElse(null);
        }

        OrderConsigneeDO consignee = new OrderConsigneeDO();
        consignee.setOrder(order);
        consignee.setConsigneeId(consigneeVO.getConsigneeId());
        consignee.setConsignee(consigneeVO.getConsignee());
        consignee.setProvinceCode(consigneeVO.getProvinceCode());
        consignee.setProvinceName(provinceName);
        consignee.setCityCode(consigneeVO.getCityCode());
        consignee.setCityName(cityName);
        consignee.setDistrictCode(consigneeVO.getDistrictCode());
        consignee.setDistrictName(districtName);
        consignee.setStreetCode(consigneeVO.getStreetCode());
        consignee.setStreetName(streetName);
        consignee.setAddress(consigneeVO.getAddress());
        consignee.setPostalCode(StringUtils.hasLength(consigneeVO.getPostalCode()) ? consigneeVO.getPostalCode().trim() : "");
        consignee.setCountryCode(consigneeVO.getCountryCode());
        consignee.setPhone(consigneeVO.getPhone());
        consignee.setTelephone(StringUtils.hasLength(consigneeVO.getTelephone()) ? consigneeVO.getTelephone().trim() : consigneeVO.getTelephone());
        consignee.setDefaultConsignee(consigneeVO.getDefaultConsignee());

        order.setConsignee(consignee);
        return Wrapper.success();
    }

    /**
     * 校验并生成订单其他要求
     *
     * @param order        订单
     * @param requirements 其他要求接口参数
     * @param isCreate     是否新增，true-新增，false-修改
     * @return 检查结果
     * @see IBaseOrderRequirementService
     */
    @Override
    public Wrapper<Void> checkOrderRequirement(OrderDO order, OpenApiRequirementVO requirements, boolean isCreate) {
        if(!isCreate) {
            orderRequirementRepository.deleteByOrder(order);
        }

        if(requirements == null) {
            order.setRequirement(null);
            return Wrapper.success();
        }

        OrderRequirementDO requirement = new OrderRequirementDO();
        requirement.setOrder(order);

        OrderRequirementDetailBO detailBO = new OrderRequirementDetailBO(requirements.getPack(), requirements.getRemark());
        requirement.setDetail(detailBO);

        order.setRequirement(requirement);

        return Wrapper.success();
    }

    /**
     * 订单发货
     *
     * @param deliveryVO 接口参数
     * @return 操作结果
     * @see IVendorOrderService
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> deliveryOrder(OpenApiOrderDeliveryVO deliveryVO) {
        OrderDO order = orderRepository.findById(deliveryVO.getOrderId()).orElse(null);
        if(order == null) {
            return Wrapper.fail(ResponseCode.ORDER_DOES_NOT_EXIST);
        }

        //Step 1 : 校验参数，保存发货记录
        Wrapper<OrderDeliveryBO> checkResult = checkVendorOrderDelivery(order, deliveryVO);
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(checkResult.getCode(), checkResult.getMessage());
        }

        // 如果没有发货商品，返回
        if(checkResult.getData().getNoDeliveryProducts()) {
            return Wrapper.success();
        }

        //Step 2 : 执行工作流任务（修改订单状态、记录内外流转记录）
        Wrapper<ProcessTaskBO> taskResult = baseOrderTaskService.execOrderProcess(order, checkResult.getData().getTaskJumpParameter());
        if(taskResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(taskResult.getCode(), taskResult.getMessage());
        }

        orderRepository.saveAndFlush(order);

        return Wrapper.success();
    }

    /**
     * 校验、生成发货记录，判断工作流跳转条件
     *
     * @param order      订单
     * @param deliveryVO 接口参数
     * @return 发货结果
     * @see IBaseOrderDeliveryService
     */
    @Override
    public Wrapper<OrderDeliveryBO> checkVendorOrderDelivery(OrderDO order, OpenApiOrderDeliveryVO deliveryVO) {
        //Step 1: 发货数量可以为0，要进行过滤
        deliveryVO.getProducts().removeIf(p -> p.getDeliveryCount().compareTo(BigDecimal.ZERO) <= 0);
        if(CollectionUtils.isEmpty(deliveryVO.getProducts())) {
            return Wrapper.success(new OrderDeliveryBO(true));
        }

        //Step 1: 查询商品
        List<OrderProductDO> orderProducts = orderProductRepository.findByOrder(order);
        if(deliveryVO.getProducts().stream().anyMatch(p -> orderProducts.stream().noneMatch(orderProduct -> orderProduct.getId().equals(p.getOrderProductId())))) {
            return Wrapper.fail(ResponseCode.ORDER_PRODUCT_DOES_NOT_EXIST);
        }

        //Step 2 : 判断是否已经全部发货完毕，并统计：差异数量=已发货-已收货，未发货=采购数量-已发货
        for (OrderProductDO orderProduct : orderProducts) {
            deliveryVO.getProducts().stream().filter(product -> product.getOrderProductId().equals(orderProduct.getId())).findFirst().ifPresent(productVO -> orderProduct.setDelivered(orderProduct.getDelivered().add(productVO.getDeliveryCount())));

            //关键：如果发货数超过购买数，剩余数量设置为0
            orderProduct.setLeftCount(NumberUtil.max(orderProduct.getQuantity().subtract(orderProduct.getDelivered()), BigDecimal.ZERO));
            orderProduct.setDifferCount(orderProduct.getDelivered().subtract(orderProduct.getReceived()));
        }

        orderProductRepository.saveAll(orderProducts);

        //Step 3 : 发货批次+1
        int batchNo = orderDeliveryRepository.findByOrder(order).stream().mapToInt(OrderDeliveryDO::getBatchNo).max().orElse(0) + 1;
        String deliveryNo = OrderStringUtil.randomUniqueNumber("FH");

        //Step 4 : 生成发货记录，记录发货商品
        OrderDeliveryDO delivery = new OrderDeliveryDO();
        delivery.setOrder(order);
        delivery.setCreateTime(StringUtils.hasLength(deliveryVO.getDeliveryTime()) ? LocalDateTime.parse(deliveryVO.getDeliveryTime(), OrderServiceContants.DEFAULT_TIME_FORMATTER) : LocalDateTime.now());
        delivery.setBatchNo(batchNo);
        delivery.setDeliveryNo(deliveryNo);
        delivery.setAddress(deliveryVO.getAddress());
        delivery.setLogisticsNo(StringUtils.hasLength(deliveryVO.getLogisticsNo()) ? deliveryVO.getLogisticsNo().trim() : "");
        delivery.setCompany(StringUtils.hasLength(deliveryVO.getCompany()) ? deliveryVO.getCompany() : "");
        delivery.setReceiptNo("");
        delivery.setReceiptTime(null);
        delivery.setVendorInnerStatus(VendorInnerStatusEnum.VENDOR_DELIVERY_CONFIRMED.getCode());
        delivery.setBuyerInnerStatus(BuyerInnerStatusEnum.BUYER_TO_RECEIVE.getCode());

        List<OrderDeliveryProductDO> deliveryProducts = deliveryVO.getProducts().stream().map(productVO -> {
            OrderDeliveryProductDO deliveryProduct = new OrderDeliveryProductDO();
            deliveryProduct.setOrderId(order.getId());
            deliveryProduct.setBatchNo(batchNo);
            deliveryProduct.setDeliveryNo(deliveryNo);
            orderProducts.stream().filter(orderProduct -> orderProduct.getId().equals(productVO.getOrderProductId())).findFirst().ifPresent(orderProduct -> {
                deliveryProduct.setOrderProductId(orderProduct.getId());
                deliveryProduct.setProductId(orderProduct.getProductId());
                deliveryProduct.setSkuId(orderProduct.getSkuId());
                deliveryProduct.setProductNo(orderProduct.getProductNo());
                deliveryProduct.setName(orderProduct.getName());
                deliveryProduct.setCategory(orderProduct.getCategory());
                deliveryProduct.setBrand(orderProduct.getBrand());
                deliveryProduct.setUnit(orderProduct.getUnit());
                deliveryProduct.setSpec(orderProduct.getSpec());
                deliveryProduct.setPrice(orderProduct.getPrice());
                deliveryProduct.setRefPrice(orderProduct.getRefPrice());
                deliveryProduct.setTaxRate(orderProduct.getTaxRate());
                deliveryProduct.setQuotedProductId(orderProduct.getMaterial() == null ? 0L : orderProduct.getMaterial().getProductId());
                deliveryProduct.setQuotedSkuId(orderProduct.getMaterial() == null ? 0L : orderProduct.getMaterial().getSkuId());
                deliveryProduct.setQuotedProductNo(orderProduct.getMaterial() == null ? "" : orderProduct.getMaterial().getProductNo());
                deliveryProduct.setQuotedName(orderProduct.getMaterial() == null ? "" : orderProduct.getMaterial().getName());
                deliveryProduct.setQuotedCategory(orderProduct.getMaterial() == null ? "" : orderProduct.getMaterial().getCategory());
                deliveryProduct.setQuotedBrand(orderProduct.getMaterial() == null ? "" : orderProduct.getMaterial().getBrand());
                deliveryProduct.setQuotedSpec(orderProduct.getMaterial() == null ? "" : orderProduct.getMaterial().getSpec());
                deliveryProduct.setQuantity(orderProduct.getQuantity());
                deliveryProduct.setDelivered(productVO.getDeliveryCount());
                deliveryProduct.setReceived(BigDecimal.ZERO);
                deliveryProduct.setDifferCount(productVO.getDeliveryCount());
            });
            return deliveryProduct;
        }).collect(Collectors.toList());

        //Step 5 : 保存
        orderDeliveryProductRepository.saveAll(deliveryProducts);
        delivery.setDeliveryProducts(new HashSet<>(deliveryProducts));
        orderDeliveryRepository.saveAndFlush(delivery);

        //Step 6: 设置订单关联，在调用方需要保存OrderDO
        order.setHasDelivered(true);
        order.getDeliveries().add(delivery);

        //Step 7: 判断流程跳转参数（跳转参数在流程图中定义）
        // A. 如果发货未完成
        //          流程不跳转，即参数为0
        // B. 如果发货完成（此时必有至少一次未收货）
        //    b-1: 有且仅有一次未收货， 跳转参数为 1，流程执行到“收货”任务，当最后一次收货完成后设置跳转参数为-1，收发货流程完成
        //    b-2: 大于一次未收货，跳转参数为 2，执行另一个回环分支，改变供应商状态
        boolean deliveryDone = false;
        int taskJumpParameter = 0;
        if (orderProducts.stream().allMatch(orderProduct -> orderProduct.getLeftCount().compareTo(BigDecimal.ZERO) == 0)) {
            deliveryDone = true;
            if(order.getDeliveries().stream().filter(d -> !d.getBuyerInnerStatus().equals(BuyerInnerStatusEnum.BUYER_CONFIRM_RECEIPT.getCode())).count() == 1) {
                taskJumpParameter = 1;
            } else {
                taskJumpParameter = 2;
            }
        }

        return Wrapper.success(new OrderDeliveryBO(taskJumpParameter, batchNo, deliveryNo, deliveryDone));
    }

    /**
     * 确认收货
     *
     * @param receiveVO 接口参数
     * @return 收货结果
     * @see com.ssy.lingxi.order.service.web.IBuyerOrderService
     */
    @Override
    public Wrapper<Void> receiveOrder(OpenApiOrderReceiveVO receiveVO) {
        OrderDO order = orderRepository.findById(receiveVO.getOrderId()).orElse(null);
        if(order == null) {
            return Wrapper.fail(ResponseCode.ORDER_DOES_NOT_EXIST);
        }

        //Step 1 : 校验参数，修改发货记录统计数据
        Wrapper<OrderReceiveBO> checkResult = checkBuyerOrderReceive(order, receiveVO.getBatchNo());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(checkResult.getCode(), checkResult.getMessage());
        }

        //Step 2 : 执行工作流任务（修改订单状态、记录内外流转记录）
        Wrapper<ProcessTaskBO> taskResult = baseOrderTaskService.execOrderProcess(order, checkResult.getData().getTaskJumpParamter());
        if(taskResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(taskResult.getCode(), taskResult.getMessage());
        }

        orderRepository.saveAndFlush(order);

        //如果订单任务的工作流任务id为空字符串，表示订单流程完成
//        if(!StringUtils.hasLength(order.getTask().getTaskId())) {
//            //订单流程完成
//        }

        return Wrapper.success();
    }

    /**
     * 校验、生成收货数据，判断工作流跳转条件
     *
     * @param order 订单
     * @param batchNo 发货批次
     * @return 收货结果
     * @see IBaseOrderDeliveryService
     */
    @Override
    public Wrapper<OrderReceiveBO> checkBuyerOrderReceive(OrderDO order, Integer batchNo) {
        //Step 1 : 查询所有发货记录，用于判断是否已经全部收货完毕
        List<OrderDeliveryDO> deliveries = orderDeliveryRepository.findByOrder(order);

        //Step 2 : 修改本次收货的发货记录
        OrderDeliveryDO delivery = deliveries.stream().filter(orderDelivery -> orderDelivery.getBatchNo().equals(batchNo)).findFirst().orElse(null);
        if(delivery == null) {
            return Wrapper.fail(ResponseCode.ORDER_DELIVERY_DOES_NOT_EXIST);
        }

        // 判断本次的发货是否已经被确认收货
        if(delivery.getBuyerInnerStatus().equals(BuyerInnerStatusEnum.BUYER_CONFIRM_RECEIPT.getCode())) {
            return Wrapper.fail(ResponseCode.ORDER_DELIVERY_HAS_BE_RECEIVED);
        }

        String receiptNo = OrderStringUtil.randomUniqueNumber("SH");
        delivery.setReceiptNo(receiptNo);
        delivery.setReceiptTime(LocalDateTime.now());
        delivery.setBuyerInnerStatus(BuyerInnerStatusEnum.BUYER_CONFIRM_RECEIPT.getCode());
        orderDeliveryRepository.saveAndFlush(delivery);

        //Step 3 ： 修改本次发货商品的数据：已收货=已发货 差异数量=已发货-已收货
        List<OrderDeliveryProductDO> deliveryProducts = orderDeliveryProductRepository.findByOrderIdAndBatchNo(order.getId(), batchNo);
        if(CollectionUtils.isEmpty(deliveryProducts) || deliveryProducts.stream().anyMatch(deliveryProduct -> !deliveryProduct.getDeliveryNo().equals(delivery.getDeliveryNo()))) {
            return Wrapper.fail(ResponseCode.ORDER_DELIVERY_PRODUCT_DOES_NOT_EXIST);
        }

        deliveryProducts.forEach(deliveryProduct -> {
            deliveryProduct.setReceived(deliveryProduct.getDelivered());
            deliveryProduct.setDifferCount(NumberUtil.max(deliveryProduct.getDelivered().subtract(deliveryProduct.getReceived()), BigDecimal.ZERO));
        });
        orderDeliveryProductRepository.saveAll(deliveryProducts);

        //Step 4 : 修改订单商品的数据：已收货 = 当前的已收货 + 发货商品的收货数 ；差异数量=已发货-已收货
        List<OrderProductDO> orderProducts = orderProductRepository.findByOrder(order);
        if(CollectionUtils.isEmpty(orderProducts)) {
            return Wrapper.fail(ResponseCode.ORDER_PRODUCT_DOES_NOT_EXIST);
        }

        orderProducts.forEach(orderProduct -> deliveryProducts.stream().filter(deliveryProduct -> deliveryProduct.getProductId().equals(orderProduct.getProductId()) && deliveryProduct.getSkuId().equals(orderProduct.getSkuId())).findFirst().ifPresent(deliveryProduct -> {
            orderProduct.setReceived(orderProduct.getReceived().add(deliveryProduct.getReceived()));
            orderProduct.setDifferCount(NumberUtil.max(orderProduct.getDelivered().subtract(orderProduct.getReceived()), BigDecimal.ZERO));
        }));

        orderProductRepository.saveAll(orderProducts);

        //设置“已经收过一次货”标记
        order.setHasReceived(true);

        //Step 5: 判断流程跳转参数（在收货的时候，有可能已经发货全部完成了，所以要以 “收货数量 - 购买数量 < 0” 为条件来判断）
        // A. 如果收货已经全部完成，则本次收货必定是最后一次收货，流程任务为“收货”任务
        //         所以跳转参数为 -1， 完成收货发货流程
        // B. 如果收货未完成：
        //    b-1: 如果发货已经完成
        //       b-1-1: 如果仅有一次未收货，跳转参数为 1
        //       b-1-1: 否则，跳转参数为2
        //    b-2: 否则，跳转参数为0

        boolean receiveDone = false;
        long unconfirmCount = orderProducts.stream().filter(orderProduct -> orderProduct.getReceived().subtract(orderProduct.getQuantity()).compareTo(BigDecimal.ZERO) < 0).count();
        int taskJumpParameter = 0;
        if(unconfirmCount == 0) {
            receiveDone = true;
            taskJumpParameter = -1;
        } else {
            if(orderProducts.stream().allMatch(orderProduct -> orderProduct.getLeftCount().compareTo(BigDecimal.ZERO) == 0)) {
                if(deliveries.stream().filter(d -> d.getBuyerInnerStatus().equals(BuyerInnerStatusEnum.BUYER_TO_RECEIVE.getCode())).count() == 1) {
                    taskJumpParameter = 1;
                } else {
                    taskJumpParameter = 2;
                }
            }
        }

        return Wrapper.success(new OrderReceiveBO(taskJumpParameter, receiptNo, receiveDone));
    }
}
